#include "state.h"
#include <iostream>

using namespace state;

GumballMachine::GumballMachine(int n)
{
    this->m_noQuarter  = new NoQuarterState(this);
    this->m_hasQuarter = new HasQuarterState(this);
    this->m_sold             = new SoldState(this);
    this->m_soldOut        = new SoldOutState(this);
    this->m_count           = n;

    if (this->m_count > 0)
    {
        this->m_curState = this->m_noQuarter;
    }
    else
    {
        this->m_curState = this->m_soldOut;
    }
}

void GumballMachine::insertQuarter()
{
    this->m_curState->insertQuarter();
}

void GumballMachine::ejectQuarter()
{
    this->m_curState->ejectQuarter();
}

void GumballMachine::turnCrank()
{
    this->m_curState->turnCrank();
    this->m_curState->dispense();
}

void GumballMachine::setState(State* s)
{
    this->m_curState = s;
}

void GumballMachine::releaseBall()
{
    std::cout << "A gumball comes rolling out the slot..." << std::endl;
    if (this->m_count > 0)
    {
        this->m_count--;
    }
}

int GumballMachine::getCount()
{
    return this->m_count;
}

State* GumballMachine::getNoQuarterState()
{
    return this->m_noQuarter;
}

State* GumballMachine::getHasQuarterState()
{
    return this->m_hasQuarter;
}

State* GumballMachine::getSoldState()
{
    return this->m_sold;
}

State* GumballMachine::getSoldOutState()
{
    return this->m_soldOut;
}

NoQuarterState::NoQuarterState(GumballMachine* g)
{
    this->m_gMachine = g;
}

void NoQuarterState::insertQuarter()
{
    std::cout << "You inserted a quarter" << std::endl;
    this->m_gMachine->setState(this->m_gMachine->getHasQuarterState());
}

void NoQuarterState::ejectQuarter()
{
    std::cout << "You haven't inserted a quarter" << std::endl;
}

void NoQuarterState::turnCrank()
{
    std::cout << "You turned, but there's no quarter" << std::endl;
}

void NoQuarterState::dispense()
{
    std::cout << "You need to pay first" << std::endl;
}

HasQuarterState::HasQuarterState(GumballMachine* g)
{
    this->m_gMachine = g;
}

void HasQuarterState::insertQuarter()
{
    std::cout << "You can't insert another quarter" << std::endl;
}

void HasQuarterState::ejectQuarter()
{
    std::cout << "Quarter returned" << std::endl;
    this->m_gMachine->setState(this->m_gMachine->getNoQuarterState());
}

void HasQuarterState::turnCrank()
{
    std::cout << "You turned..." << std::endl;
    this->m_gMachine->setState(this->m_gMachine->getSoldState());
}

void HasQuarterState::dispense()
{
    std::cout << "No gumball dispensed" << std::endl;
}

SoldState::SoldState(GumballMachine* g)
{
    this->m_gMachine = g;
}

void SoldState::insertQuarter()
{
    std::cout << "Please wait, we're already giving you a gumball" << std::endl;
}

void SoldState::ejectQuarter()
{
    std::cout << "Sorry, you already turned the crank" << std::endl;
}

void SoldState::turnCrank()
{
    std::cout << "Turning twice doesn't get you another gumball!" << std::endl;
}

void SoldState::dispense()
{
    this->m_gMachine->releaseBall();
    if (this->m_gMachine->getCount() > 0)
    {
        this->m_gMachine->setState(this->m_gMachine->getNoQuarterState());
    }
    else
    {
        std::cout << "Oops, out of gumballs!" << std::endl;
        this->m_gMachine->setState(this->m_gMachine->getSoldOutState());
    }
}

SoldOutState::SoldOutState(GumballMachine* g)
{
    this->m_gMachine = g;
}

void SoldOutState::insertQuarter()
{
    std::cout << "You can't insert a quarter, the machine is sold out" << std::endl;
}

void SoldOutState::ejectQuarter()
{
    std::cout << "You can't eject, you haven't inserted a quarter yet" << std::endl;
}

void SoldOutState::turnCrank()
{
    std::cout << "You turned, but there are no gumballs" << std::endl;
}

void SoldOutState::dispense()
{
    std::cout << "No gumball dispensed" << std::endl;
}
